package jmine.tec.utils.reflection.fast;

import java.io.InvalidClassException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

import jmine.tec.utils.reflection.fast.reflection.FastMethodImpl;

/**
 * FastMethod baseado em um {@link Method}
 * 
 * @author takeshi
 */
public abstract class AbstractFastMethod implements FastMethod, Serializable {

    private final Method method;

    /**
     * C'tor
     * 
     * @param method {@link Method}
     */
    public AbstractFastMethod(Method method) {
        super();
        this.method = method;
    }

    /**
     * {@inheritDoc}
     */
    public final Class<?>[] getArgumentTypes() {
        return this.method.getParameterTypes();
    }

    /**
     * {@inheritDoc}
     */
    public final int getArity() {
        return this.method.getParameterTypes().length;
    }

    /**
     * {@inheritDoc}
     */
    public final Method getMethod() {
        return this.method;
    }

    /**
     * {@inheritDoc}
     */
    public final String getName() {
        return this.method.getName();
    }

    /**
     * {@inheritDoc}
     */
    public final boolean isStatic() {
        return Modifier.isStatic(this.method.getModifiers());
    }

    /**
     * Method used by the standard java serialization mechanism. By default, we serialize as a {@link SerializedForm}
     * 
     * @return Object
     */
    Object writeReplace() {
        return new SerializedForm(method);
    }

    /**
     * Serialized version of an {@link AbstractFastMethod}
     * 
     * @author takeshi
     */
    public static class SerializedForm implements Serializable {
        private final Class<?> declaringClass;

        private final String methodName;

        private final Class<?>[] declaredParameterTypes;

        /**
         * Plain constructor
         * 
         * @param m {@link Method}
         */
        public SerializedForm(Method m) {
            super();
            this.declaringClass = m.getDeclaringClass();
            this.methodName = m.getName();
            this.declaredParameterTypes = m.getParameterTypes();
        }

        /**
         * This method is called by the java serialization mechanism. This class replaces itself with the reflection version of
         * {@link FastMethod}
         * 
         * @return {@link FastMethodImpl}
         * @throws ObjectStreamException if the class has been modified
         */
        Object readResolve() throws ObjectStreamException {
            try {
                Method m = declaringClass.getDeclaredMethod(methodName, declaredParameterTypes);
                return new FastMethodImpl(m);
            } catch (NoSuchMethodException e) {
                throw new InvalidClassException("Method with name: " + methodName + " and types: "
                        + Arrays.toString(declaredParameterTypes) + " not found on type: " + declaringClass);
            }
        }

    }

}
